package zlog

import (
	"fmt"
	"gitee.com/xlizy/common-go/utils/threadlocal"
	rotatelogs "github.com/lestrrat-go/file-rotatelogs"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"os"
	"strings"
	"time"
)

var _log *zap.Logger
var _space map[int]string

type Logger struct {
	*zap.SugaredLogger
}

func InitLogger(path string) {
	_space = make(map[int]string, 45)
	for i := 0; i < 45; i++ {
		v := ""
		for j := 0; j < i; j++ {
			v += " "
		}
		_space[i] = v
	}
	//获取编码器
	encoder := getJsonEncoder()
	multiWriteSyncer := zapcore.NewMultiWriteSyncer(getWriterSyncer(path), zapcore.AddSync(os.Stdout))
	core := zapcore.NewCore(encoder, multiWriteSyncer, zap.InfoLevel)
	//生成Logger
	_log = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1), zap.AddStacktrace(zap.ErrorLevel))
	defer func(_log *zap.Logger) {
		_ = _log.Sync()
	}(_log)
}

func GetLogger() *zap.Logger {
	return _log
}

// core 三个参数之  Encoder 编码
func getEncoder() zapcore.Encoder {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeTime = timeEncoder
	encoderConfig.EncodeCaller = customCallerEncoder
	return zapcore.NewConsoleEncoder(encoderConfig)
}

func getJsonEncoder() zapcore.Encoder {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeTime = timeEncoder
	return zapcore.NewJSONEncoder(encoderConfig)
}

func timeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
	enc.AppendString(t.Format("2006-01-02 15:04:05.000"))
}

func customCallerEncoder(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) {
	enc.AppendString("[" + appendTraceId() + "]")
	path := caller.TrimmedPath()
	formatLen := 35
	if len(path) < formatLen {
		path += _space[formatLen-len(path)]
	}
	enc.AppendString("[" + path + "]")
}

func getWriterSyncer(path string) zapcore.WriteSyncer {
	//lumberWriteSyncer := &lumberjack.Logger{
	//	Filename:   path,
	//	MaxSize:    30, // megabytes
	//	MaxBackups: 1000,
	//	MaxAge:     1, // days
	//	LocalTime:  true,
	//	Compress:   false, //Compress确定是否应该使用gzip压缩已旋转的日志文件。默认值是不执行压缩。
	//}

	datePath := strings.Replace(path, ".log", "", -1) + "_%Y-%m-%d.log"
	//保存日志30天，每1分钟分割一次日志
	hook, _ := rotatelogs.New(
		datePath,
		// 为最新的日志建立软连接，指向最新日志文件
		rotatelogs.WithLinkName(path),

		// 清理条件： 将已切割的日志文件按条件(数量or时间)直接删除
		//--- MaxAge and RotationCount cannot be both set  两者不能同时设置
		//--- RotationCount用来设置最多切割的文件数(超过的会被 从旧到新 清理)
		//--- MaxAge 是设置文件清理前的最长保存时间 最小分钟为单位
		//--- if both are 0, give maxAge a default 7 * 24 * time.Hour
		// WithRotationCount和WithMaxAge两个选项不能共存，只能设置一个(都设置编译时不会出错，但运行时会报错。也是为了防止影响切分的处理逻辑)
		rotatelogs.WithRotationCount(30), // 超过这个数的文件会被清掉
		//rotatelogs.WithMaxAge(time.Hour*24*30), // 保存多久(设置文件清理前的最长保存时间 最小分钟为单位)

		// 切分条件(将日志文件做切割；WithRotationTime and WithRotationSize ~~两者任意一个条件达到都会切割~~)
		// 经过亲测后发现，如果日志没有持续增加，WithRotationTime设置较小(如10s)，并不会按WithRotationTime频次切分文件。当日志不停增加时，会按照WithRotationTime设置来切分(即便WithRotationTime设置的很小)
		rotatelogs.WithRotationTime(24*time.Hour), // 10秒分割一次(设置日志切割时间间隔,默认 24 * time.Hour)
		//rotatelogs.WithRotationSize(int64(1*1024*1024*1024)), // 文件达到多大则进行切割，单位为 bytes；
	)

	return zapcore.AddSync(hook)
}

func appendTraceId() string {
	traceId := threadlocal.GetTraceId()
	if traceId == "<nil>" {
		traceId = "00000000-0000-0000-0000-000000000000"
	}
	return traceId
}

func Debug(template string, args ...interface{}) {
	_log.With(zap.String("traceId", appendTraceId())).Debug(fmt.Sprintf(replace(template), args...))
}

func Info(template string, args ...interface{}) {
	_log.With(zap.String("traceId", appendTraceId())).Info(fmt.Sprintf(replace(template), args...))
}

func Warn(template string, args ...interface{}) {
	_log.With(zap.String("traceId", appendTraceId())).Warn(fmt.Sprintf(replace(template), args...))
}

func Error(template string, args ...interface{}) {
	_log.With(zap.String("traceId", appendTraceId())).Error(fmt.Sprintf(replace(template), args...))
}

func replaceOld(template string) string {
	return strings.Replace(template, "{}", "%v", -1)
}

func replace(template string) string {
	return strings.Replace(template, "{}", "%v", -1)
}
